package com.uraurora.crescent.function.functools.memoizer;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

import com.uraurora.crescent.tuple.ITuple;

import java.util.concurrent.*;

public abstract class AbstractMemoizer<RT> {

    protected final Cache<ITuple, Future<RT>> cache;

    protected AbstractMemoizer(Cache<ITuple, Future<RT>> cache){
        this.cache = cache;
    }

    protected AbstractMemoizer(int cacheMaxSize){
        this.cache = CacheBuilder.newBuilder()
                .maximumSize(cacheMaxSize)
                .softValues()
                .build();
    }

    protected AbstractMemoizer(){
        this.cache = CacheBuilder.newBuilder()
                .maximumSize(128)
                .softValues()
                .build();
    }

    protected RT compute(ITuple arg, Callable<RT> task) {
        while(true){
            try {
                Future<RT> future = cache.get(arg, () -> {
                    FutureTask<RT> f = new FutureTask<>(task);
                    f.run();
                    return f;
                });
                return future.get();
            } catch (CancellationException e){
                cache.invalidate(arg);
            } catch (ExecutionException | InterruptedException e) {
                throw new IllegalStateException(e);
            }
        }
    }

//    protected RT compute(ITuple arg, Callable<RT> task){
//        while(true){
//            Future<RT> result = cache.get(arg);
//            if (result == null) {
//                FutureTask<RT> ft = new FutureTask<>(task);
//                result = cache.putIfAbsent(arg, ft);
//                if(result==null){result = ft; ft.run();}
//            }
//            try {
//                return result.get();
//            } catch (CancellationException e){
//                cache.remove(arg, result);
//            } catch (ExecutionException | InterruptedException e) {
//                throw new IllegalStateException(e);
//            }
//        }
//    }

    //<editor-fold desc="PUBLIC METHODS">
    public void cacheClear(){
        cache.cleanUp();
    }

    public long cacheSize(){
        return cache.size();
    }
    //</editor-fold>

}
